#include "BuildTerrain.h"

extern cTerrainBuilder* g_pTerrainBuilder;

cTerrainBuilder::cTerrainBuilder():m_texScale(1.0f)
{
	
}
void cTerrainBuilder::ReadHeightFiles(string filename)
{
	cHeightmapMgr* pHeightmapMgr = GetHeightmapMgr();
	pHeightmapMgr->PushHeightmap(filename,GridRows,GridCols,-1250.0f,20.0f,"Heightmap0");
	m_pHeightMap = pHeightmapMgr->GetHeightmap("Heightmap0");
}
void cTerrainBuilder::BuildTerrain(string grasslocation,string treelocation)
{
	ID3DXMesh* pGlobalTerrain;
	int iFaceRows = GridRows - 1;
	int iFaceCols = GridCols - 1;
	HR(D3DXCreateMeshFVF(iFaceRows*iFaceCols*4,GridRows*GridCols+iFaceRows*iFaceCols,
		D3DXMESH_32BIT|D3DXMESH_SYSTEMMEM,TerrainFVF,m_pDevice9,&pGlobalTerrain));
	vector<D3DXVECTOR3> vecPos;
	GenerateGridPos(GridRows,GridCols,TileWidth,TileDepth,Vec3Center,vecPos);
	TerrainVertex* pVertex;
	pGlobalTerrain->LockVertexBuffer(0,(void**)&pVertex);
	int iIndex = 0;
	for(int i = 0;i!=GridRows;++i)
	{
		for(int j = 0;j!=GridCols;++j)
		{
			iIndex = i*GridCols + j;
			pVertex[iIndex].pos = vecPos[iIndex];
			pVertex[iIndex].pos.y = m_pHeightMap->GetHeight(i,j);
			pVertex[iIndex].tex = D3DXVECTOR2(i,j)*m_texScale;
			pVertex[iIndex].texBlend.x = (float)j/GridRows;
			pVertex[iIndex].texBlend.y = (float)i/GridCols;
			if(i==0||j==0||i==GridRows-1||j==GridCols - 1){
				pVertex[iIndex].normal = D3DXVECTOR3(0.0f,1.0f,0.0f);
				continue;
			}
			pVertex[iIndex].normal.x = m_pHeightMap->GetHeight(i,j+1) - m_pHeightMap->GetHeight(i,j-1);
			pVertex[iIndex].normal.z = m_pHeightMap->GetHeight(i+1,j) - m_pHeightMap->GetHeight(i-1,j);
			pVertex[iIndex].normal.y = 30;
			D3DXVec3Normalize(&pVertex[iIndex].normal,&pVertex[iIndex].normal);
		}
	}
	//Get The location of grass and tree here
	size_t size = GridRows*GridCols;
	char* Map = new char[size];
	LoadRaw("../../files/textures/grasslocation.raw",&Map,size);
	GetLocation(pVertex,grasslocation,&Map);
	LoadRaw("../../files/textures/treelocation.raw",&Map,size);
	GetLocation(pVertex,treelocation,&Map);
	delete [] Map;

	++iIndex;
	for(int i = 0;i!=iFaceRows;++i)
	{
		for(int j = 0;j!=iFaceCols;++j)
		{
			pVertex[iIndex].pos = vecPos[iIndex];
			pVertex[iIndex].pos.y = (pVertex[i*GridCols + j].pos.y + pVertex[i*GridCols + j + 1].pos.y +
									  pVertex[(i+1)*GridCols+ j].pos.y + pVertex[(i+1)*GridCols + j + 1].pos.y)/4;
			pVertex[iIndex].tex =  (pVertex[i*GridCols + j].tex + pVertex[(i+1)*GridCols + j + 1].tex)/2;
			pVertex[iIndex].texBlend =  (pVertex[i*GridCols + j].texBlend + pVertex[(i+1)*GridCols + j + 1].texBlend)/2;
			pVertex[iIndex].normal = (pVertex[i*GridCols + j].normal + pVertex[i*GridCols + j + 1].normal + 
					pVertex[(i+1)*GridCols + j].normal + pVertex[(i+1)*GridCols + j + 1].normal)/4;
			D3DXVec3Normalize(&pVertex[iIndex].normal,&pVertex[iIndex].normal);
			++iIndex;
		}
	}

	D3DXVECTOR3 center = Vec3Center;
	center.x = center.x - TileWidth/2 + DW/2;
	center.z = center.z + TileDepth/2 - DD/2;
	for(int i = 0;i!=TileRows;++i)
	{
		center.x = center.x - TileWidth/2 + DW/2;
		for(int j = 0;j!=TileCols;++j)
		{
			cTerrainBase* pTerrainChunk = new cTerrainBase;
			pTerrainChunk->SetSubGrid(i,j,GridRows,GridCols);
			pTerrainChunk->CreateGridFromVertex(ChunkRows,ChunkCols,ChunkWidth,ChunkDepth,center,pVertex);
			mapTerrainTile.push_back(pTerrainChunk);
			center.x += DW;
		}
		center.z -= DD;
	}
	pGlobalTerrain->UnlockVertexBuffer();
	SAFE_RELEASE(pGlobalTerrain);
}
void cTerrainBuilder::OnInit(string grasslocation,string treelocation)
{
	m_pDevice9 = GetDevice9();
	m_pTextureMgr = GetTextureMgr();
	m_pShaderMgr = GetShaderMgr();
	ReadHeightFiles("../../files/textures/terrain1.raw");
	SetTextureScale(0.1f);
	BuildTerrain(grasslocation,treelocation);
	SetTexture(L"");
	SetShader(L"");
}
void cTerrainBuilder::Render()
{
	for( int i = 0; i != mapVisibleTerrainTile.size(); ++i )
	{
		mapVisibleTerrainTile[ i ]->Render();
	}
}
void cTerrainBuilder::Update(float dt)
{
	cCamera* pCamera = GetCamera();
	mapVisibleTerrainTile.clear();
	for(int i = 0;i!=mapTerrainTile.size();++i)
	{
		if( !pCamera->CheckVisible( mapTerrainTile[i]->GetSphere() ) ){
			mapTerrainTile[i]->SetDrawState(false);
			continue;
		}
		else {
			mapTerrainTile[i]->SetDrawState(true);
			mapVisibleTerrainTile.push_back( mapTerrainTile[ i ] );
		}
		mapTerrainTile[i]->Update(dt);
	}
}
void cTerrainBuilder::Release()
{
	delete m_pHeightMap;
	for(int i = 0;i!=mapTerrainTile.size();++i)
	{
		mapTerrainTile[i]->Release();
	}
	mapVisibleTerrainTile.clear();
}
void cTerrainBuilder::SetTexture(WCHAR* srcfiles)
{
	m_pTextureMgr->PushTex(L"../../files/textures/ground/sand3.jpg","sand0");
	m_pTextureMgr->PushTex(L"../../files/textures/ground/dirt.dds","dirt0");
	m_pTextureMgr->PushTex(L"../../files/textures/ground/grass.dds","grass0");
	m_pTextureMgr->PushTex(L"../../files/textures/blend1.jpg","blend");
	for(int i = 0;i!=mapTerrainTile.size();++i){
		mapTerrainTile[i]->PushTexture("grass0");
		mapTerrainTile[i]->PushTexture("sand0");
		mapTerrainTile[i]->PushTexture("dirt0");
		mapTerrainTile[i]->PushTexture("blend");
	}
}
float cTerrainBuilder::GetHeight( float x, float z )
{
	float tc = ( x + TileWidth * 0.5 ) /  DW;
	float tr = ( z - TileDepth * 0.5 ) / -DD;

	int c = int( floorf( tc ) );
	int r = int( floorf( tr ) );

	float upleft = m_pHeightMap->GetHeight( r, c );
	float upright = m_pHeightMap->GetHeight( r, c + 1 );
	float bottomleft = m_pHeightMap->GetHeight( r + 1, c );
	float bottomright = m_pHeightMap->GetHeight( r + 1, c + 1 );

	float coffset = tc - float( c );
	float roffset = tr - float( r );

	if( roffset < 1.0f - coffset ){
		float cvector = upright - upleft;
		float rvector = bottomleft - upleft;
		return upleft + coffset * cvector + roffset * rvector;
	}
	else{
		float cvector = bottomleft - bottomright;
		float rvector = upright - bottomright;
		return bottomright + ( 1.0f - coffset ) * cvector + ( 1.0f - roffset ) * rvector;
	}
}

void cTerrainBuilder::SetShader(WCHAR* srcfiles)
{
	m_pShaderMgr->PushShader(L"../../files/fxFiles/terrain.fx","terrain");
	for(int i = 0;i!=mapTerrainTile.size();++i)
	{
		mapTerrainTile[i]->OnInit("terrain");
	}
}

void cTerrainBuilder::GetLocation(TerrainVertex* pVertex,string grasslocation,char** inFile)
{
	unsigned char c;
	ofstream fout(grasslocation,fstream::out|fstream::trunc);
	char min = 10;
	DWORD index = 0;
	for(int i = 0;i!=GridRows;++i)
	{
		for(int j = 0;j!=GridCols;++j)
		{
			index = i*GridCols+j;
			c = (*inFile)[index];
			if(c<=min){
				fout<<pVertex[index].pos.x<<" "
					<<pVertex[index].pos.y<<" "
					<<pVertex[index].pos.z<<" "
					<<endl;
			}
		}
	}
}

//get terrin height
float GetTerrainHeight( float x, float z )
{
	return g_pTerrainBuilder->GetHeight( x, z );
}

vector<cTerrainBase*>& GetVisibleTerrainTile()
{
	return g_pTerrainBuilder->GetVisibleTerrainTile();
}
